#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
日行情数据提供者（专业回测用）

- 从本地真实K线缓存(stock_data_cache/stock_data.db)读取数据
- 计算前收盘、涨跌停价，判断停牌
- 提供 MarketState（与 constraints 模块配合）

注意：
- 本模块不产生任何模拟数据，完全依赖本地真实历史K线
- 涨跌幅限制暂按10%默认值；后续可注入更精细的限制率提供者（ST/科创等）
"""
from __future__ import annotations

import sqlite3
from dataclasses import dataclass
from datetime import datetime, date
from typing import Optional

from .constraints import MarketState, compute_limit_prices
from local_data_cache_manager import LocalDataCacheManager


@dataclass
class DailyBar:
    code: str
    trade_date: str  # YYYY-MM-DD
    open: float
    high: float
    low: float
    close: float
    volume: int


class DailyMarketDataProvider:
    """基于本地缓存DB的日行情提供者"""

    def __init__(self, limit_rate_provider: Optional[callable] = None):
        # 提供根据股票代码/日期返回涨跌幅限制率的函数
        # 默认规则：
        # - ST 股票：5%
        # - 科创板/创业板/注册制：20%
        # - 其他A股主板：10%
        def _default_limit_rate(code: str, d: str) -> float:
            code = (code or "").strip()
            # 简单规则（可替换为从DB/标志表读取）：
            if code.startswith("ST") or code.startswith("*ST"):
                return 0.05
            # 科创板688/689、创业板300系列通常20%
            if code.startswith(("688", "689", "300")):
                return 0.20
            return 0.10

        self._limit_rate_provider = limit_rate_provider or _default_limit_rate
        # 复用LocalDataCacheManager以获取数据库路径
        self._cache = LocalDataCacheManager()
        self._db_path = self._cache.db_path  # stock_data_cache/stock_data.db

    def _get_prev_close(self, code: str, trade_date: str) -> Optional[float]:
        sql = "SELECT close FROM stock_kline WHERE code=? AND date<? ORDER BY date DESC LIMIT 1"
        try:
            with sqlite3.connect(self._db_path, timeout=15.0) as conn:
                cur = conn.cursor()
                cur.execute(sql, (code, trade_date))
                row = cur.fetchone()
                return float(row[0]) if row and row[0] is not None else None
        except Exception:
            return None

    def _get_bar(self, code: str, trade_date: str) -> Optional[DailyBar]:
        sql = "SELECT date, open, high, low, close, volume FROM stock_kline WHERE code=? AND date=? LIMIT 1"
        try:
            with sqlite3.connect(self._db_path, timeout=15.0) as conn:
                cur = conn.cursor()
                cur.execute(sql, (code, trade_date))
                row = cur.fetchone()
                if not row:
                    return None
                return DailyBar(
                    code=code,
                    trade_date=str(row[0]),
                    open=float(row[1]),
                    high=float(row[2]),
                    low=float(row[3]),
                    close=float(row[4]),
                    volume=int(row[5] or 0),
                )
        except Exception:
            return None

    @staticmethod
    def _to_str_date(d: date | datetime | str) -> str:
        if isinstance(d, str):
            return d
        if isinstance(d, datetime):
            return d.strftime("%Y-%m-%d")
        return d.strftime("%Y-%m-%d")

    def get_market_state(self, code: str, d: date | datetime | str) -> Optional[MarketState]:
        """返回给定代码和日期的市场状态（前收、涨跌停、停牌）。"""
        trade_date = self._to_str_date(d)
        prev_close = self._get_prev_close(code, trade_date)
        if prev_close is None:
            return None  # 无法计算

        bar = self._get_bar(code, trade_date)
        # volume 为 0 或当日无bar，视为停牌
        suspended = (bar is None) or (bar.volume == 0)

        limit_rate = float(self._limit_rate_provider(code, trade_date) or 0.10)
        up, down = compute_limit_prices(prev_close, limit_rate)
        return MarketState(pre_close=prev_close, upper_limit=up, lower_limit=down, suspended=suspended)

